﻿using System;
using System.Collections.Generic;
using System.Collections;
using CzfSDK;
using System.Threading.Tasks;

namespace Server
{
	public class Room
	{
		public static bool autoNPC = true;

		// 技术统计
		public const byte K_Role_AttCount = 23;
		public const byte K_Role_AttHitCount = 24;
		public const byte K_Role_Def = 25;
		public const byte K_Role_Harm = 26;
		public const byte K_Role_Energy = 27;
		public const byte K_Role_Servant_Harm = 32;

		const byte FRAMEID = 255;
		const byte K_RoleId = 1;

		public static Dictionary<string, Room> rooms = new Dictionary<string, Room>();
		public static object lock_rooms = new object ();

		public string id;
		public Dictionary<int, Hashtable> boxScores = new Dictionary<int, Hashtable> (); // 技术统计

		public List<User> users = new List<User> ();	    // 所有角色

		public List<User> diedUsers = new List<User> ();    // 已死亡的角色
		public List<User> offlineUsers = new List<User>();  // 已经掉线的角色

		public List<User> teamL = new List<User> ();
		public List<User> teamR = new List<User> ();

		public bool end = false;

		public int maxCount;
		public int npcCount;
		public List<int> npcIds;
		public string type;

		public RoomWait wait;
		public int category = -1;  // 0-2v2 1-1v1 其他-训练赛

		public Room (string id, int maxCount, int npcCount, string type, RoomWait wait)
		{
			this.wait = wait;
			this.id = id;
			this.maxCount = maxCount;
			lock (lock_rooms) {
				rooms [id] = this;
			}

			leftTa = rightTa = 1;

			this.type = type;

			category = -1;
			if (type == RoomManager.TYPE_2V2)
				category = 0;
			else if(type == RoomManager.TYPE_1V1)
				category = 1;


			this.npcCount = npcCount;
			npcIds = new List<int> ();
			if (npcCount > 0) {				
				// 加入NPC
				for (int i = 0; i < npcCount; i++) {
					NPCIn ();
				}
			}
			ThreadTool.TaskRun (NPCCheck);
		}

		bool roomStarted = false;
		// 每多长时间会加入一个NPC
		void NPCCheck()
		{
			while (!roomStarted) {
				ThreadTool.TaskDelay(20 * 1000);
				if (autoNPC && !roomStarted) {
					NPCIn ();
				}
			}
		}

		void NPCIn()
		{
			var npc = new User (UserManager.Get("NPC"), null);
			npc.id = -1000 - npcIds.Count;
			npcIds.Add (npc.id);
			UserIn (npc, false);
		}

		public void UserIn(User user, bool outRoom = true)
		{
//			CzfLog.Info ("room:" + id + " UserIn:" + user.id);
			if (outRoom && user.room != null)
				user.room.UserOut (user);

			users.Add (user);
			user.room = this;

			if (users.Count >= maxCount) {
				Start ();
				wait.room = null;
			}

			bool blnAllNpc = true;
			foreach (var u in users) {
				if (u.id > 0) {
					blnAllNpc = false;
					break;
				}
			}

			if (blnAllNpc) {
				wait.room = null;
				Destroy ();
			}
		}

		public void UserOut(User user)
		{
//			CzfLog.Info ("room:" + id + " UserOut:" + user.id);
			users.Remove (user);
			user.room = null;
			if (users.Count == 0) {
				end = true;
			}
		}

		Hashtable formations = new Hashtable (); // 每个玩家的阵容信息

		public void Start()
		{
			roomStarted = true;
			end = false;
			var tick = DateTime.Now.Ticks; // 随机数种子
			var idList = new List<int>(users.Count);
			var names = new List<string>(users.Count);

			// 将玩家分成2组
			for (int i = 0; i < users.Count; i++) {
				if (i % 2 == 0)
					teamL.Add (users[i]);
				else
					teamR.Add (users[i]);
				idList.Add (users[i].id);
			}

			// 拼出战斗开始协议参数
			var ht = new Hashtable ();
			ht ["idList"] = idList;
			ht ["tick"] = tick;

			// 随机出双方的地图
			var allMap = MapManager.GetAll();
			int total = 0;
			foreach (var m in allMap) {
				total += m.GetVal<int> ("weight");
			}

			int randL = CzfRandom.Range (total);
			int randR = CzfRandom.Range (total);
			string mapL = null;
			string mapR = null;
			foreach (var m in allMap) {
				if (mapL == null) {
					randL -= m.GetVal<int> ("weight");
					if (randL <= 0) {
						mapL = m.GetVal<string> ("info");
					}
				}

				if (mapR == null) {
					randR -= m.GetVal<int> ("weight");
					if (randR <= 0) {
						mapR = m.GetVal<string> ("info");
					}
				}
			}
			if (mapL == null || mapR == null) {
				throw new CzfProtoException (Constants.ERR1,  "没有随机到地图!!!");
			}

			ht ["mapL"] = mapL;
			ht ["mapR"] = mapR;

			var htUsers = new Hashtable ();

			int userStars = 0;

			// 每个玩家的阵容信息
			formations = new Hashtable ();
			foreach (var user in users) {
				var u = UserManager.Get(user.id);
				var f = FormationManager.Get (u.GetVal<int>("nowformation"));
				formations [user.id] = f;

				var htUser = new Hashtable ();
				// 胜点
				if (category >= 0) {
					if (user.id > 0) {
						var userArena = ArenaManager.GetUserArena (user.id, category);
						htUser ["star"] = userArena.GetVal<int> ("stars");
						userStars = (int)htUser ["star"];
					}
				} else {
					if (user.id > 0) {
						var userArena = ArenaManager.GetUserArena (user.id, 1);
						htUser ["star"] = userArena.GetVal<int> ("stars");
						if (user.id > 0)
							userStars = (int)htUser ["star"];
					}
				}

				htUser ["id"] = user.id; // id
				htUser ["roleid"] = f ["roleid"];                       // 所使用的角色id

				if (user.id > 0) {
					htUser ["name"] = user.name; // 名字
				} else {
					// npcs随机名字
					user.name = UserManager.GetRandomName();
					htUser ["name"] = user.name;
				}

				names.Add (user.name);

				htUser ["team"] = (teamL.Contains (user)) ? 1 : 0;    // 所属队伍: 0,1
				htUsers[user.id] = htUser;
			}

			// 给NPC和玩家一样的胜点
			foreach (var user in users) {
				if (user.id < 0) {
					var htUser = (Hashtable)htUsers [user.id];
					htUser ["star"] = userStars;
				}
			}
			ht ["names"] = names;
			ht ["users"] = htUsers;
			ht ["formations"] = formations;
			ht ["npcIds"] = npcIds;
			ht ["type"] = type;
//			if (wait.type == RoomManager.TYPE_1Single)
//				ht ["standAlone"] = true;

			int index = 0;
			// 推送战斗开始信息
			foreach (var user in users) {
				if (user.client != null) {
					ht ["userId"] = user.id;
					ht ["index"] = index;
					CzfNetManager.current.Call (user.client, "StartFight", null, 0, ht);
					index++;
				}
			}

			ThreadTool.TaskRun (FrameSyncTask, TaskCreationOptions.LongRunning);
		}

		// 有角色死亡
		public void UserDie(int userId)
		{
//			CzfLog.Info ("UserDie:" + userId);
			foreach(var user in teamL){
				if (user.id == userId) {
					diedUsers.Add (user);
					CheckOver ();
					return;
				}
			}

			foreach(var user in teamR){
				if (user.id == userId) {
					diedUsers.Add (user);
					CheckOver ();
					return;
				}
			}
			CheckOver ();
		}

		// 有塔死亡
		public void TaDie(int userId)
		{
//			CzfLog.Info ("TaDie:" + userId);
			bool left = false;
			foreach(var user in teamL){
				if (user.id == userId) {
					left = true;
					break;
				}
			}

			if (left)
				leftTa--;
			else
				rightTa--;
			CheckOver ();
		}

		public string GetIds(List<User> list)
		{
			string str = "";
			foreach (var user in list) {
				str += user.id + ",";
			}
			return str.TrimEnd(',');
		}

		public string GetUsersId()
		{
			return GetIds (users);
		}

		Hashtable userReadys = new Hashtable();

		public Hashtable ReadyProgress(int userId, int progress)
		{
			userReadys [userId] = progress;
			return userReadys;
		}

		#region 帧同步逻辑
		void FrameSyncTask()
		{
			startTime = DateTime.Now;
			#region 等待玩家准备完毕
			foreach (var u in users) {
				if (u.id > 0) {
					userReadys [u.id] = 0;
				} else {
					userReadys [u.id] = 100;
				}
			}

			int waitTime = 0;

			while(waitTime < 5000){
				ThreadTool.TaskDelay(500);

				bool allReady = true;
				foreach (int ready in userReadys.Values) {
					if (ready < 100) {
						allReady = false;	
						break;
					}
				}

				if (allReady)
					break; // 所有人准备完毕

				waitTime += 500;
			}
			#endregion

			#region 3秒倒计时
			#endregion

			frameSyncData [FRAMEID] = sendTime;
			CzfLog.Info ("room({0}) start users:{1}".CzfFormat(id, GetUsersId()));
			var frameRate = ConfigManager.current ["config"].Get<int> ("FrameRate", 100);
			while (!end || endCount < 10) {
				if (end)
					endCount++;

				var time1 = DateTime.Now;
				SendFrameSyncData ();
				var time2 = DateTime.Now;
				var delay = Convert.ToInt32(frameRate - (time2 - time1).TotalMilliseconds);
				if(Game.DEBUG_ROOM)
					CzfLog.Info ("delay({0})".CzfFormat(delay));
				
				if(delay > 0)
					ThreadTool.TaskDelay(delay);
				else
					ThreadTool.TaskDelay(10);
			}

			SendEnd ();

			CzfLog.Info ("room({0}) end".CzfFormat(id));

			// 将所有人退出房间
			for (int i = 0; i < users.Count; i++) {
				UserOut (users [i]);
				i--;
			}

			Destroy ();
		}

		DateTime startTime;

		int endCount = 0; // 战斗结束后再推送一段时间

		Hashtable frameSyncData = new Hashtable (); // 每个玩家的同步数据
		int waitTime = 0; // 如果有玩家卡顿,那么等待玩家的次数
		object lock_frame = new object();
		// 收到同步数据
		public void ReceiveFrameSyncData(User user, Hashtable data)
		{
			lock (lock_frame) {
				foreach (var val in data.Values) {
					if (val is Hashtable) {
						var ht = (Hashtable)val;
						var userId = ht.GetVal<int> (K_RoleId);

						if (ht.ContainsKey (K_Role_AttCount)) {
							// 技术统计
							AddBoxScore (userId, 
								ht.GetVal<int> (K_Role_AttCount), 
								ht.GetVal<int> (K_Role_AttHitCount), 
								ht.GetVal<int> (K_Role_Def), 
								ht.GetVal<int> (K_Role_Harm),
								ht.GetVal<int> (K_Role_Servant_Harm),
								ht.GetVal<int> (K_Role_Energy)
							);

							ht.Remove (K_Role_AttCount);
							ht.Remove (K_Role_AttHitCount);
							ht.Remove (K_Role_Def);
							ht.Remove (K_Role_Harm);
							ht.Remove (K_Role_Servant_Harm);
							ht.Remove (K_Role_Energy);
						}

						if (frameSyncData.ContainsKey (userId)) {
							var tb = (Hashtable)frameSyncData [userId];
							tb.Merge (ht);
						} else {
							frameSyncData [userId] = ht;
						}


					}
				}

				// 掉线重连
				if (offlineUsers.Contains (user)) {
					offlineUsers.Remove (user);
//					CzfLog.Info ("offlineUsers.Remove ({0})".CzfFormat(user.id));
				}
			}
		}

		User GetUser(int userId)
		{
			foreach (var user in users) {
				if (user.id == userId)
					return user;
			}

			return null;
		}

		#region 技术统计
		// 记录技术统计
		void AddBoxScore(int userId, int attcount, int atthit, int def, int harm, int servantHarm, int energy)
		{
			Hashtable boxScore;
			if (!boxScores.ContainsKey (userId)) {
				var user = GetUser (userId);
				if (user == null)
					return;

				var f = formations.GetVal<Hashtable> (userId); // 玩家阵容

				// 初始化技术统计
				boxScore = new Hashtable ();
				boxScore ["id"] = user.id;                              // 玩家id
				boxScore ["name"] = user.name;                          // 玩家名字
				boxScore ["skills"] = f ["skills"];                       // 所使用的技能 技能id,技能id,技能id...
				boxScore ["roleid"] = f ["roleid"];                       // 所使用的角色id
				boxScore ["team"] = (teamL.Contains (user)) ? 1 : 0;    // 所属队伍: 0,1

				boxScore ["attcount"] = 0;                              // 射出多少箭
				boxScore ["atthit"] = 0;                                // 命中多少箭
				boxScore ["def"] = 0;                                   // 抵挡多少伤害
				boxScore ["harm"] = 0;                                  // 造成多少伤害
				boxScore ["servantharm"] = 0;                                  // 造成多少召唤物伤害
				boxScore ["score"] = 0;                                 // 评价分
				boxScore ["defrate"] = 0;                               // 抵挡伤害占比
				boxScore ["harmrate"] = 0;                              // 造成伤害占比
				boxScore ["energy"] = 0;                                // 能量获得
				boxScore ["energyrate"] = 0;                            // 能量获得占比

				boxScore ["mvp"] = 0;                                   // mvp
				boxScore ["maxharm"] = 0;                               // 最强输出
				boxScore ["maxhitrate"] = 0;                            // 最强命中
				boxScore ["maxdef"] = 0;                                // 最强抵挡

				this.boxScores [user.id] = boxScore;
			} else {
				boxScore = boxScores[userId];
			}

			boxScore ["attcount"] = attcount;
			boxScore ["atthit"] = Math.Min (attcount, atthit);
			boxScore ["def"] = def;
			boxScore ["harm"] = harm;
			boxScore ["servantharm"] = servantHarm;
			boxScore ["energy"] = energy;
		}

		// 计算全场最佳
		Hashtable CheckMaxScore(List<User> winTeam)
		{
			// 计算最高输出,最高抵挡,最高命中率
			Hashtable maxHarm = null;
			float maxHarmScore = 0;

			Hashtable maxDef = null;
			float maxDefScore = 0;

			Hashtable maxHitRate = null;
			float maxHitRateScore = 0;

			// 计算全场总输出,总抵挡,总能量
			float totalHarm = 0;
			float totalDef = 0;
			float totalEnergy = 0;

			foreach (var ht in boxScores.Values) {
				var harmScore = ht.GetVal<float> ("harm");
				var sharmScore = ht.GetVal<float> ("servantharm");
				var defScore = ht.GetVal<float> ("def");
				var energy = ht.GetVal<float> ("energy");
				var hitRateScore = ht.GetVal<float> ("atthit") / ht.GetVal<float> ("attcount");

				totalHarm += harmScore + (sharmScore / 2);
				totalDef += defScore;
				totalEnergy += energy;

				if (maxHarmScore <= harmScore) {
					maxHarm = ht;
					maxHarmScore = harmScore;
				}
				if (maxDefScore <= defScore) {
					maxDef = ht;
					maxDefScore = defScore;
				}
				if (maxHitRateScore <= hitRateScore) {
					maxHitRate = ht;
					maxHitRateScore = hitRateScore;
				}
			}

			// 计算评分,MVP
			Hashtable mvp = null;
			float maxMvpScore = -1;

			foreach (var ht in boxScores.Values) {
				var harmScore = ht.GetVal<float> ("harm");
				var sharmScore = ht.GetVal<float> ("servantharm");
				var defScore = ht.GetVal<float> ("def");
				var energy = ht.GetVal<float> ("energy");

				var harmRate = totalHarm == 0 ? 0 : (harmScore + (sharmScore/2)) / totalHarm;
				var defRate = totalDef == 0 ? 0 : defScore / totalDef;
				var energyRate = totalEnergy == 0 ? 0 : energy / totalEnergy;
				var attcount = ht.GetVal<float> ("attcount");
				var hitRateScore = attcount == 0 ? 0 : ht.GetVal<float> ("atthit") / attcount;

				var mvpScore = (harmRate * 6) + (defRate * 3) + (hitRateScore * 1);

				ht ["harm"] = harmScore + (sharmScore/2);
				ht ["harmrate"] = harmRate;
				ht ["defrate"] = defRate;
				ht ["energyrate"] = energyRate;
				ht ["score"] = mvpScore;

				if (maxMvpScore <= mvpScore) {
					var user = GetUser (ht.GetVal<int> ("id"));
					if (winTeam.Contains (user)) { 
						// 只有获胜方可以获得MVP
						mvp = ht;
						maxMvpScore = mvpScore;
					}
				}
			}

			if(mvp != null)
				mvp ["mvp"] = 1;

			if(maxHarm != null)
				maxHarm ["maxharm"] = 1;

			if(maxDef != null)
				maxDef ["maxdef"] = 1;

			if(maxHitRate != null)
				maxHitRate ["maxhitrate"] = 1;

			var ret = new Hashtable ();
			foreach (var k in boxScores.Keys) {
				ret [k] = boxScores [k];
			}

			LogSkillWin (winTeam);

			return ret;

		}

		// 统计技能的胜负情况
		void LogSkillWin(List<User> winTeam)
		{
			if (category < 0)
				return; // 训练赛不记录

			foreach (int userId in formations.Keys) 
			{
				if (userId > 0) {
					var user = GetUser (userId);
					var f = formations.GetVal<Hashtable> (userId); // 玩家阵容
					var skillds = f.GetVal<string> ("skills").Split (',');                     // 所使用的技能 技能id,技能id,技能id..

					if (winTeam.Contains (user)) {
						foreach (string skillId in skillds) {
							if (skillId.Trim () != "") {
								SkillManager.SkillWin (Convert.ToInt32 (skillId));
							}
						}
					} else {
						foreach (string skillId in skillds) {
							if (skillId.Trim () != "") {
								SkillManager.SkillLose (Convert.ToInt32 (skillId));
							}
						}
					}
				}
			}
		}



		#endregion

		float sendTime = 0; // 发送同步数据次数

//		DateTime last;
		// 发送同步数据
		void SendFrameSyncData()
		{
			lock (lock_frame) {
				if (CheckFrameSyncData()) {

//					CzfLog.Info ("Gap:"+(DateTime.Now - last).TotalMilliseconds);
//					last = DateTime.Now;

					var bytes = CzfNetManager.current.proto.Pack ("SyncCallBack", null, 0, frameSyncData);
					foreach (var user in users) {
						if (user.client != null) {
							user.client.Send (bytes);
						}
					}

					sendTime ++;
					frameSyncData.Clear ();
					frameSyncData [FRAMEID] = sendTime;
					if (sendTime > 0 && sendTime % 100 == 0) {
						CheckOver ();
//						CzfLog.Info ("room({0}) send {1} times users:{2}".CzfFormat(id, sendTime, GetUsersId()));
					}
				}
			}
		}

		// 检测是否已经收到所有人的同步数据
		bool CheckFrameSyncData()
		{
			if (frameSyncData.Count < (users.Count - diedUsers.Count - offlineUsers.Count)) {
				waitTime++;
				if (waitTime >= 2) {
					waitTime = 0;
					// 有人超时,判断为掉线状态
//					CzfLog.Info ("有人超时, frameSyncData={0}, users={1}, diedUsers={2}, offlineUsers={3}".CzfFormat(
//						frameSyncData.Count,
//						users.Count,
//						diedUsers.Count,
//						offlineUsers.Count
//					));

					foreach (var user in users) {
						if (!frameSyncData.ContainsKey (user.id)) {
							offlineUsers.Add (user);
//							CzfLog.Info ("offlineUsers.Add ({0})".CzfFormat(user.id));
						}
					}
					CheckOver ();
//					return true;
				} else {
//					return false;
				}
			} else {
				waitTime = 0;
//				return true;
			}

			return true; // 不等待卡的玩家
		}
		#endregion

		int leftTa = 0;
		int rightTa = 0;

		// 发送战斗结束信息
		void SendEnd()
		{
			int cost = Convert.ToInt32 ((DateTime.Now - startTime).TotalSeconds);

			// 拼出战斗结束协议参数
			var ht = new Hashtable ();
			// 技术统计
			ht["boxscore"] = CheckMaxScore (leftWin ? teamL : teamR);

			foreach (var user in teamL) {
				Hashtable prize = null;
				if (user.id > 0) {
					LogManager.FightLog (user.id, cost, category);
					if (category >= 0) {
						prize = ArenaManager.FightEnd (user.id, category, leftWin);
					} else {
						//						prize = ArenaManager.FightEnd (user.id, 1, leftWin); // 训练赛也可以升降级
					}
				}
				if (user.client != null) {
					ht ["isWin"] = (leftWin ? 1 : 0);
					ht ["prize"] = prize;
					CzfNetManager.current.Call (user.client, "EndFight", null, 0, ht);
				}
			}

			foreach (var user in teamR) {
				Hashtable prize = null;
				if (user.id > 0) {
					LogManager.FightLog (user.id, cost, category);
					if (category >= 0) {
						prize = ArenaManager.FightEnd (user.id, category, !leftWin);
					} else {
						//						prize = ArenaManager.FightEnd (user.id, 1, !leftWin); // 训练赛也可以升降级
					}
				}
				if (user.client != null) {
					ht ["isWin"] = (leftWin ? 0 : 1);
					ht ["prize"] = prize;
					CzfNetManager.current.Call (user.client, "EndFight", null, 0, ht);
				}
			}
			//				CzfLog.Info ("room({0}) end, users:({1}), diedUsers:({2}), offlineUsers:({3}), boxScores:{4},category:{5}".CzfFormat(
			//					id,
			//					GetIds(users),
			//					GetIds(diedUsers),
			//					GetIds(offlineUsers),
			//					boxScores.ToJson(),
			//					category
			//				));
		}

		bool leftWin;
		// 判断是否战斗结束
		void CheckOver()
		{
			// 判断是否战斗结束
			bool teamLAllDie = true;
			bool teamRAllDie = true;
			foreach (var user in teamL) {
				if (!diedUsers.Contains (user)) {
					teamLAllDie = false;
					break;
				}
			}

			foreach (var user in teamR) {
				if (!diedUsers.Contains (user)) {
					teamRAllDie = false;
					break;
				}
			}

			// 超过5分钟强制结束房间
			if (leftTa == 0 || rightTa == 0 || teamLAllDie || teamRAllDie || sendTime >=(2500)) {
				leftWin = true;
				if (leftTa == 0 || teamLAllDie)
					leftWin = false;
				
				// 战斗结束
				end = true;
			}
		}

		public void Destroy()
		{
			lock (lock_rooms) {
				rooms.Remove (id);
			}
//			CzfLog.Info ("room({0}) Destroy".CzfFormat(id));
		}

	}
}

